package baseFx.tests.common.lock;

import baseFx.common.AutoCloseable;
import baseFx.common.concurrent.Counter;
import baseFx.common.concurrent.Task;
import baseFx.common.lock.AbortTimeoutException;
import baseFx.common.lock.AppLockspace;
import baseFx.common.lock.Lockspace;
import baseFx.common.lock.redis.RedisLockspace;
import baseFx.common.utils.FxDate;
import org.junit.Assert;
import org.junit.Test;
import redis.clients.jedis.JedisPool;

import java.util.UUID;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;

public class ConcurrentLockspaceTest {
    @Test
    public void 并发锁定() throws InterruptedException {
        Lockspace lockspace = getLockspace();
        int max = 5;
        int tCount = 10;
        CountDownLatch latch = new CountDownLatch(tCount);
        CyclicBarrier cyclic = new CyclicBarrier(tCount);
        for (int i = 0; i < tCount; i++) {
            Task.run(state -> {
                try {
                    cyclic.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println("start " + new FxDate() + " " + state);
                try (AutoCloseable ac = lockspace.concurrentLock("aaa", max, null)) {
                    Task.delay(2000);
                } catch (AbortTimeoutException e) {
                    e.printStackTrace();
                }
                System.out.println("end " + new FxDate() + " " + state);
                latch.countDown();
            }, i);
        }
        latch.await();
    }

    @Test
    public void 超时抛异常() throws InterruptedException {
        Counter counter = new Counter();
        CountDownLatch latch = new CountDownLatch(2);
        Semaphore semaphore = new Semaphore(0);
        String key = UUID.randomUUID().toString();
        Task.run(() -> {
            try (AutoCloseable release = getLockspace().concurrentLock(key, 1)) {
                semaphore.release();
                Task.delay(2000);
                counter.add(1);
            }
            latch.countDown();
        });
        Task.run(() -> {
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try (AutoCloseable release = getLockspace().concurrentLock(key, 1, 1)) {
                //...
            } catch (AbortTimeoutException e) {
                counter.add(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
            latch.countDown();
        });
        latch.await();
        Assert.assertEquals(counter.get(), 2);
    }

    @Test
    public void 超时自动释放() {

        String key = "autoRelease";
        getLockspace().concurrentLock(key, 1);
        try {
            getLockspace().concurrentLock(key, 1, 1);
            System.out.println("1");
        } catch (AbortTimeoutException e) {
            System.out.println("2");
            pool.close();
            pool = new JedisPool();
            redisLockspace = new RedisLockspace(pool);
            RedisLockspace.heartbeat(pool);
        }
        try {
            getLockspace().concurrentLock(key, 1, 1);
            System.out.println("3");
        } catch (AbortTimeoutException e) {
            System.out.println("4");
            Task.delay(FxDate.SECONDS * 15);

            try (AutoCloseable ac = getLockspace().concurrentLock(key, 1, 1)) {
                key = null;
                System.out.println("5");
            } catch (AbortTimeoutException abortTimeoutException) {
                System.out.println("6");
                //...
            }
        }
        Assert.assertNull(key);
//        RedisLockspace.heartbeat(pool);
//        Task.delay(FxDate.DAY);
//        pool.getResource().del("|_RCLOCK@autoRelease");
    }

    private AppLockspace appLockspace = new AppLockspace();
    private JedisPool pool = new JedisPool();
    private RedisLockspace redisLockspace = new RedisLockspace(pool);

    protected Lockspace getLockspace() {
        return redisLockspace;
    }
}
